package 缓存工具;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 缓存管理器类：用于缓存数据并支持设置过期时间。
 */
public class CacheManager {
    /**
     * 使用线程安全的 ConcurrentHashMap 作为底层存储结构。
     * key 是缓存的唯一标识符（String），value 是封装了实际数据和过期时间的 CacheWrapper 对象。
     */
    private static final Map<String, Object> cache = new ConcurrentHashMap<>();

    /**
     * 将一个对象放入缓存，并指定其过期时间。
     *
     * @param key 缓存键（String）
     * @param value 缓存值（泛型 T）
     * @param expireSeconds 过期时间（秒）
     */
    public static <T> void put(String key, T value, int expireSeconds) {
        // 将原始值封装到 CacheWrapper 中，并计算过期时间戳
        cache.put(key, new CacheWrapper<>(value, expireSeconds));
    }

    /**
     * 从缓存中根据 key 获取对应的数据，并尝试将其转换为指定类型。
     *
     * @param key 缓存键
     * @param type 数据类型（Class<T>），如 String.class、User.class 等
     * @return 如果缓存存在且未过期，则返回对应的泛型 T 类型数据；否则返回 null
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(String key, Class<T> type) {
        // 从缓存中取出 CacheWrapper 对象。使用通配符 ? 表示不关心包装中的具体类型。
        // 因为我们会在后续通过 getValue() 拿出值后进行类型转换。
        CacheWrapper<?> wrapper = (CacheWrapper<?>) cache.get(key);

        // 如果缓存不存在或已过期，就移除它并返回 null
        if (wrapper == null || wrapper.isExpired()) {
            cache.remove(key);
            return null;
        }

        // 使用 type.cast() 方法将获取到的值强制转换为指定的类型 T。
        // 优点：相比直接 (T) wrapper.getValue() 更加类型安全。
        // 如果类型不匹配会抛出异常，而不是静默失败。
        return type.cast(wrapper.getValue());
    }

    /**
     * 内部类：封装缓存值及其过期时间。
     * 泛型 T 表示缓存值的实际类型。
     */
    private static class CacheWrapper<T> {
        /**
         * 实际缓存的数据
         */
        private final T value;

        /**
         * 过期时间的时间戳（毫秒）
         */
        private final long expireTime;

        /**
         * 构造方法：创建一个带有过期时间的缓存包装对象。
         *
         * @param value 缓存值
         * @param expireSeconds 过期时间（秒）
         */
        CacheWrapper(T value, int expireSeconds) {
            this.value = value;
            this.expireTime = System.currentTimeMillis() + expireSeconds * 1000L; // 转换为毫秒
        }

        /**
         * 判断当前缓存是否已经过期。
         *
         * @return true 如果当前时间超过过期时间
         */
        boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }

        /**
         * 获取缓存中的原始值。
         *
         * @return 缓存值 T 类型
         */
        T getValue() {
            return value;
        }
    }
}